05. 规划与执行系统设计
本章高频面试题
- 什么是 Planning?它和 Chain-of-Thought 有什么区别?
- ReAct、Plan-Then-Act、ReAct + 轻规划、Tree / Graph Planning 的区别是什么?
- 什么是 Deep Agents 模式?它和传统 ReAct 有何不同?
- 为什么很多 Agent 失败并不是模型不会推理,而是规划和执行系统设计得不对?
- 什么是 Durable Execution?为什么长时 Agent 离不开它?
- 一个生产级 Agent 的执行 runtime 应该负责什么?
- 如何设计可恢复、可重试、可重规划的多步执行系统?
- 如何处理工具超时、格式错误、部分失败、重复调用和并行步骤?
- 状态机、DAG、Graph 编排、循环执行各自适合什么场景?
- 如何防止 Agent 在执行过程中跑偏、卡死或无限循环?Circuit breaker 怎么设计?
1. 什么是 Planning
Planning 可以简单理解成:
在真正执行之前,先把任务拆成若干步骤,并明确每一步做什么、依赖什么、成功标准是什么。
它的关注点不是”解释我为什么这么想”,而是:
- 下一步做什么
- 先后顺序是什么
- 失败了怎么办
- 哪一步需要调用工具
- 哪一步需要人来确认
因此,Planning 更接近”任务编排”,而不是普通的推理展开。
2. Planning 和 Chain-of-Thought 的区别
Chain-of-Thought 是模型把自己的推理过程写出来。
Planning 关注的是”可执行的任务结构”。
同一个任务:“调研 3 篇关于 RAG + RL 的论文并输出中文总结”
CoT 可能会写:
- 我需要先找相关论文
- 然后筛选出代表性的
- 再总结它们的优缺点
Planning 更像:
- 用关键词检索论文
- 按时间和相关性筛选
- 对每篇抽取方法、实验和结论
- 生成中文对比总结
- 如果检索结果不足 3 篇,则扩展关键词再搜一次
一个好记的说法:
CoT 更像”把想法写出来”,Planning 更像”生成可以执行的任务表”。
3. 常见规划范式
3.1 ReAct
ReAct = Reason + Act:模型先思考当前应该做什么 → 调一个工具 → 看工具结果 → 再继续下一轮。
优点:简单、灵活、适合快速搭 Demo。 缺点:缺少全局前瞻、容易局部最优、任务一长就漂移、对多步失败恢复支持弱。
3.2 Plan-Then-Act
先生成较完整计划,再按计划执行。
优点:更可控、更适合长任务、便于审计和恢复。 缺点:环境变化时不够灵活,前面生成的计划可能很快过时。
3.3 ReAct + 轻规划
最实用的折中方案:先粗计划,执行时按阶段动态调整。 优点:比纯 ReAct 稳,比强规划灵活。 适合:中等复杂度任务、工具较多但还没有复杂搜索树的场景。
3.4 Tree / Graph Planning(ToT、LATS)
不是只走一条路径,而是探索多条候选路径再做选择。 优点:不容易卡死在第一条错误路径,适合高不确定性任务。 缺点:成本高、延迟高、实现复杂。 在工业里通常只用于高价值、高难度任务。
3.5 Deep Agents:2025 年流行起来的新范式
Deep Agents(LangChain 社区提出、Claude Code 在实践中验证)核心是三件套:
- 显式 Planning tool:让模型把 plan 写进一个可见、可修改的
todo_write/plan工具,而不是藏在 CoT 里 - File system / scratchpad:中间结果、长文、草稿写到”虚拟文件系统”,主 context 只保留摘要和引用
- Sub-agents:把独立子任务派给 sub-agent,主 agent 只看汇报
为什么这套模式有效:
- Plan 变成数据(可 diff、可 resume、可 eval),而不是模型一次性生成的 CoT
- Context window 不再是瓶颈——长内容 offload 到 scratchpad
- 主 agent 的 trajectory 保持短而清晰
Claude Code 能跑几千步不漂移,核心就是这套。任何 long-horizon task(代码编写、研究报告、深度调研)都可以借鉴。
4. 为什么规划与执行系统是 Agent 的核心
很多初学者会觉得:“模型够强,直接一步一步自己想就好了。”
但工程里真正的问题是:
- 工具可能失败
- 中间状态会变
- 用户会打断任务
- 有些步骤需要审批
- 有些步骤能并行,有些不能
- 进程可能崩溃
- 机器可能重启
这些都不是单纯靠模型”想得更聪明”就能解决的。它们本质上是运行时设计问题。
成熟 Agent 系统里:
- 模型负责局部判断和内容生成
- 运行时负责状态、重试、恢复、路由和持久化
5. Durable Execution:长时 Agent 的地基
如果你的 Agent 任务可能运行数分钟到数小时、可能跨多个进程、可能需要人工审批后继续,那就离不开 durable execution。
5.1 Durable Execution 做了什么
核心能力:
- 每一步状态都持久化:checkpoint 到存储,崩溃后能从最近 checkpoint 恢复
- 决策重放确定性:同一个 state 喂给同一个 agent,行为应该可重现(至少对非 LLM 节点是这样)
- Interrupt / resume:任务能在任意节点暂停,等待外部事件(人工审批、webhook)后恢复
- Exactly-once 语义:副作用动作(发邮件、打款)即使 runtime 崩溃也不会重复执行
- 长时等待不占资源:挂起的 task 只占存储,不占内存/连接
5.2 主流选型
- LangGraph:内建 checkpointer(MemorySaver、PostgresSaver、RedisSaver 等)、原生
interrupt()/Command(resume=...)模式、天然适配 LLM agent - Temporal:老牌 durable execution 平台,跨语言、成熟,SDK 覆盖 TS/Python/Go/Java
- Inngest:偏事件驱动、serverless 友好、TS 生态好
- Restate:较新的 durable execution runtime,主打轻量和低延迟
- 自研 event-sourced state machine:当业务需求非常定制、不想引入新依赖时
选型原则:
- 主 agent 在 LangGraph,副作用动作(发邮件、打款、publish)外挂到 Temporal/Inngest 这类专业 durable queue,两者互补
- 不要把 durable execution 当成简单的”重试库”——核心价值是 state-as-data
6. 执行系统应该承担哪些职责
一个生产级执行 runtime 至少应该负责:
- 持有当前状态(结构化、可持久化)
- 按规则调度节点或步骤
- 调模型和工具
- 超时、重试、降级
- 记录事件和日志
- 支持中断和恢复(interrupt / resume)
- 支持人工审批
- 输出可观察 trace
- 副作用幂等(idempotency key、去重)
- Budget 控制(步数、token、花费、延迟的 circuit breaker)
换句话说,执行系统不是”包一层 while 循环”那么简单。
7. 执行状态该怎么设计
7.1 不要只依赖 message history
很多 Agent Demo 只依赖消息列表,但一旦任务复杂:
- 历史消息不等于任务状态
- 历史消息不便于校验
- 历史消息不便于恢复
更稳的做法是显式定义结构化状态:
type TaskStatus =
| "pending"
| "running"
| "waiting_human"
| "completed"
| "failed"
| "cancelled";
type AgentRunState = {
runId: string;
objective: string;
plan: Array<{
id: string;
title: string;
status: "pending" | "running" | "done" | "failed" | "skipped";
retryCount: number;
dependsOn: string[];
idempotencyKey?: string;
}>;
currentStepId?: string;
status: TaskStatus;
errors: Array<{
stepId?: string;
code: string; // 结构化错误码,不是自由文本
message: string;
retryable: boolean;
retryAfterMs?: number;
}>;
budget: {
maxSteps: number;
maxTokens: number;
maxCostUsd: number;
usedSteps: number;
usedTokens: number;
usedCostUsd: number;
};
approvedActions: string[];
};好处:
- 更容易恢复
- 更容易做事件流
- 更容易判断”现在到底在哪一步”
- 更容易做 budget 控制
7.2 把状态和消息分开
推荐的做法:
messages用来和模型对话state用来驱动任务执行
LangGraph 的 State + MessagesValue 模式就是这个思路的具体化。
8. 规划与执行的常见架构选择
8.1 线性步骤流
适合:明确固定流程、小规模 MVP。 优点:简单。缺点:很难表达复杂分支。
8.2 DAG / Workflow
适合:依赖关系明确、某些步骤可以并行。 优点:可视化清晰、并行友好。缺点:对复杂动态回环不够自然。
8.3 Graph / State Machine
适合:长任务、复杂分支、需要中断恢复、混合人工审批。 优点:控制力强,更适合真实 Agent runtime。 代表:LangGraph、XState、自研状态机。
8.4 Plan-as-Data
最现代的做法:把 plan 当成结构化数据(JSON schema 定义的数组),模型用 update_plan / complete_step 这种工具去读写它。plan 可以 diff、可 resume、可 eval,这就是 Deep Agents 的核心思路。
9. 如何处理失败:重试、回滚、重规划
生产系统里最关键的部分之一。
9.1 先区分错误类型
- 临时错误:网络波动、超时、429 → 退避重试
- 数据错误:返回格式不合法、缺字段、工具返回空 → schema 级重试或换策略
- 逻辑错误:选错工具、计划不合理、前后依赖破坏 → 重规划
- 外部系统错误:下游 API 挂了 → 降级或切换路径
- 预算错误:超步数/超成本 → 直接中断,不重试
不同错误不应该一律”重试三次”。
9.2 可重试错误的规范做法
- 记录失败事件(结构化错误码)
- 指数退避 + jitter(避免雪崩)
- 尊重
retry_after_ms(工具返回的限流提示) - 带 idempotency key(避免重复副作用)
- 超过阈值后进入降级或重规划
- Circuit breaker:同一下游连续失败 N 次 → 整个 circuit 打开一段时间
9.3 不可重试错误
例如参数错误、权限不足、schema 不匹配。继续重试没有意义:
- 直接失败
- 或进入局部重规划
9.4 局部重规划优先于全局重来
例如任务是:1) 搜论文 → 2) 获取全文 → 3) 总结
如果某篇 PDF 抓取失败,不应该整条链路重来,而是:
- 切到摘要模式
- 或换一篇候选论文
9.5 Critic / Reflection loop
生产系统里常见的一个模式是加一个”批评者”节点:
- 每 N 步或每个 milestone 后,让一个轻量 LLM 或 rule engine 检查当前进度
- 判断:“是否还在解决原问题""是否已经偏离目标""前几步是否有重复劳动”
- 偏离时触发 replan 或 abort
这是 Reflexion 论文的核心思路,也是 Deep Agents 里的常见做法。
10. 如何避免无限循环和任务漂移(Circuit Breaker)
ReAct 类 Agent 最常见的问题。推荐的多层 circuit breaker:
10.1 硬性预算
- 最大步数(典型 50-200)
- 最大 token 消耗
- 最大花费(美元)
- 最大 wall-clock 时间
任一超限立即中断 run。
10.2 工具级约束
- 同一 tool 连续调用 N 次以上告警 → 可能陷入循环
- 同一 tool 在整个 run 中累计 > M 次告警 → 可能缺少外部信息源
- 同一参数组合重复调用直接拒绝(缓存上次结果)
10.3 Progress assessment
每 K 步强制 agent 输出”当前目标 / 已完成 / 下一步 / 预期剩余步数”。连续几轮”预期剩余步数”不下降就触发 replan。
10.4 Deadlock detection
如果 state.plan 所有 pending 步骤都依赖未完成的前置步骤,且最近 M 步没有新的完成事件 → 死锁,人工介入。
核心原则:
不要把”停止”完全交给模型自己决定。
11. 并行执行怎么设计
并行不是越多越好。先判断步骤之间有没有依赖。
适合并行:检索多个来源、并发抓取多个候选文档、对多个独立对象做同类分析。 不适合并行:下游依赖上游结果、需要顺序审批、会互相争抢共享资源。
并行执行的工程要点:
- Concurrency pool / semaphore:限制并发数,避免打爆下游
- 超时:每个并行任务独立超时,聚合时不被最慢的一个拖死(可以用
Promise.allSettled+ 超时包装) - Failure isolation:一个子任务失败不应该让整个 gather 崩掉
- Backpressure:聚合层处理不过来时下发限流
- Rate limit awareness:并行发 API 要遵守 provider 的 RPM/TPM 限制
12. TypeScript 示例:带重试 + 幂等 + budget 的执行器
type StepResult<T> =
| { ok: true; value: T; tokensUsed?: number; costUsd?: number }
| {
ok: false;
retryable: boolean;
retryAfterMs?: number;
error: string;
code: string;
};
type PlanStep<T> = {
id: string;
title: string;
idempotencyKey: string;
run: (ctx: { idempotencyKey: string }) => Promise<StepResult<T>>;
maxRetries?: number;
};
type Budget = {
remainingSteps: number;
remainingTokens: number;
remainingCostUsd: number;
};
export async function executeStep<T>(
step: PlanStep<T>,
budget: Budget
): Promise<T> {
if (budget.remainingSteps <= 0) {
throw new Error("budget_exhausted: steps");
}
budget.remainingSteps -= 1;
const maxRetries = step.maxRetries ?? 2;
let attempt = 0;
while (attempt <= maxRetries) {
const result = await step.run({ idempotencyKey: step.idempotencyKey });
if (result.ok) {
budget.remainingTokens -= result.tokensUsed ?? 0;
budget.remainingCostUsd -= result.costUsd ?? 0;
return result.value;
}
if (!result.retryable) {
throw new Error(`[${step.id}] ${result.code}: ${result.error}`);
}
if (attempt === maxRetries) {
throw new Error(`[${step.id}] retries_exhausted: ${result.error}`);
}
const backoff =
result.retryAfterMs ??
Math.min(30_000, 300 * 2 ** attempt + Math.random() * 200);
await sleep(backoff);
attempt += 1;
}
throw new Error(`[${step.id}] unreachable`);
}
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}体现的工程原则:
- 区分可重试 / 不可重试(
retryable字段) - 错误码结构化(
code),不是自由文本 - 指数退避 + jitter
- 尊重上游
retry_after_ms - 幂等 key 显式传入,跨重试保持同一个
- Budget 显式追踪,超限直接中断
13. 什么时候用 LangGraph 这类框架
当执行系统需要下面这些能力,就值得考虑图式 runtime:
- 长时间运行
- 持久化恢复(checkpoint)
interrupt()/Command(resume=...)- 多分支和复杂回环
- human-in-the-loop
- 更强的事件流(stream modes: values / updates / messages / custom / tools / debug)
LangGraph 的 interrupt 模式(2025 版)把 HITL 做得很干净:节点内调 interrupt(payload) 暂停执行,状态持久化到 checkpointer,外部决策到位后用 graph.invoke(new Command({ resume: decision }), { configurable: { thread_id } }) 继续。同一 thread 可以暂停数月后在另一台机器上恢复。
14. 本章方法论小结
- Planning 是可执行任务编排,不等于 CoT;把 plan 做成数据而不是推理展开
- Deep Agents 模式(planning tool + scratchpad + sub-agents)是长任务的当前最佳实践
- Agent 可靠性很大程度取决于执行 runtime,而不是只取决于模型
- Durable execution 是长时 agent 的地基:state-as-data、checkpoint、interrupt/resume、exactly-once
- 显式状态比纯消息历史更适合复杂任务,状态必须包含 idempotency key 和 budget
- 失败处理要区分错误类型,优先局部重规划;可重试错误走指数退避 + jitter + idempotency
- 多层 circuit breaker(步数/token/成本/工具调用次数/progress assessment)是避免失控的硬保障
- 并行要配 concurrency pool、独立超时、failure isolation、backpressure
- Graph / State Machine 更适合复杂、长时间运行的 Agent;副作用动作外挂到 Temporal/Inngest 最稳
- 一个成熟 Agent 的执行系统必须支持重试、恢复、事件、审计、budget 和 exactly-once